day zero
Fox! Fox! Burning bright! In the forests of the night! Hint:
$ break *0x7c00
Recon
Page displays an animated fire and a hint. If you request it with a cURL user-agent curl/7.54.0
you get back the :fire: as ANSI art. Stripping all the hash #
symbols and ANSI escape sequences from the output gives you a base64 blob. Decoding the base64 blob gives you an UU-encoded file:
begin 644 boot.bin
M^C'`CMB.P([0O`!\0`^B#R#`@^#[@\@"#R+`#R#@#0`&#R+@OO9\Z+X`9@^Z
MX1ES3;X8?>BQ`+\`?C'`S18\#70:/`AU#X'_`'Y^[KY&?>B6`$_KY:JT#LT0
MZ]Z!_Q!^=<\/*`;P?0\H'@!^Z#0`9@_O'N!]9@\X%]MT"NNSOB5]Z&0`Z_Z^
M4'T/*`8`?@\H'.@/``\I'(/&$('^X'UUZ>FM`&8/[])F#^_8N^4VM'/!Z`?V
M\X@FOGQF#SK?R$5F#W#)_P_&T!!F#^_"@#;'?)QX\68/[\$X_'0'9@\XW-CK
MSF8/.-W8P[0.K#1"=`8QV\T0Z_/#3T@5)V(W,B4P(R8G)F(D,"TO8A`!=F(V
M*BLQ8CLG(S!C0D]($B,Q,34M,"9X8D)/2`$M+R=B(",A*6(U*S8J8B-B+RTF
M)S`L8@$2%V)X$D)*8DI"D)"0D)"0T(/KBQ^!OZ,P,!19EYHU$!Q"0^0^+?8=
MLGYZ',K_"4>'+N_UK&=A-F;*%C\%\==(/3KT`Z,2Z9H#""'!!8^R$@7;]B-X
M,!IB!^0G^\"KVJA;B3LZ[UWH*Z&N[\1C>BI>!*:]<K/H%+3,";[B@>;E72IV
M>$Z%?*Q4`F_Q'B"KDWX.2B^Y@Z8F-2$8/0MDV'.]]_X:@#91.)"@-!%M,%Y2
15&UY@+FE"I<D#2W\-@V55:H`
`
end
Decoding this gives you boot.bin
(use something like uudecode blob.uu
).
$ xxd boot.bin
00000000: fa31 c08e d88e c08e d0bc 007c 400f a20f .1.........|@...
00000010: 20c0 83e0 fb83 c802 0f22 c00f 20e0 0d00 ........".. ...
00000020: 060f 22e0 bef6 7ce8 be00 660f bae1 1973 .."...|...f....s
00000030: 4dbe 187d e8b1 00bf 007e 31c0 cd16 3c0d M..}.....~1...<.
00000040: 741a 3c08 750f 81ff 007e 7eee be46 7de8 t.<.u....~~..F}.
00000050: 9600 4feb e5aa b40e cd10 ebde 81ff 107e ..O............~
00000060: 75cf 0f28 06f0 7d0f 281e 007e e834 0066 u..(..}.(..~.4.f
00000070: 0fef 1ee0 7d66 0f38 17db 740a ebb3 be25 ....}f.8..t....%
00000080: 7de8 6400 ebfe be50 7d0f 2806 007e 0f28 }.d....P}.(..~.(
00000090: 1ce8 0f00 0f29 1c83 c610 81fe e07d 75e9 .....).......}u.
000000a0: e9ad 0066 0fef d266 0fef d8bb e536 b473 ...f...f.....6.s
000000b0: c1e8 07f6 f388 26be 7c66 0f3a dfc8 4566 ......&.|f.:..Ef
000000c0: 0f70 c9ff 0fc6 d010 660f efc2 8036 c77c .p......f....6.|
000000d0: 9c78 f166 0fef c138 fc74 0766 0f38 dcd8 .x.f...8.t.f.8..
000000e0: ebce 660f 38dd d8c3 b40e ac34 4274 0631 ..f.8......4Bt.1
000000f0: dbcd 10eb f3c3 4f48 1527 6237 3225 3023 ......OH.'b72%0#
00000100: 2627 2662 2430 2d2f 6210 0176 6236 2a2b &'&b$0-/b..vb6*+
00000110: 3162 3b27 2330 6342 4f48 1223 3131 352d 1b;'#0cBOH.#115-
00000120: 3026 7862 424f 4801 2d2f 2762 2023 2129 0&xbBOH.-/'b #!)
00000130: 6235 2b36 2a62 2362 2f2d 2627 302c 6201 b5+6*b#b/-&'0,b.
00000140: 1217 6278 1242 4a62 4a42 9090 9090 9090 ..bx.BJbJB......
00000150: d083 eb8b 1f81 bfa3 3030 1459 979a 3510 ........00.Y..5.
00000160: 1c42 43e4 3e2d f61d b27e 7a1c caff 0947 .BC.>-...~z....G
00000170: 872e eff5 ac67 6136 66ca 163f 05f1 d748 .....ga6f..?...H
00000180: 3d3a f403 a312 e99a 0308 21c1 058f b212 =:........!.....
00000190: 05db f623 7830 1a62 07e4 27fb c0ab daa8 ...#x0.b..'.....
000001a0: 5b89 3b3a ef5d e82b a1ae efc4 637a 2a5e [.;:.].+....cz*^
000001b0: 04a6 bd72 b3e8 14b4 cc09 bee2 81e6 e55d ...r...........]
000001c0: 2a76 784e 857c ac54 026f f11e 20ab 937e *vxN.|.T.o.. ..~
000001d0: 0e4a 2fb9 83a6 2635 2118 3d0b 64d8 73bd .J/...&5!.=.d.s.
000001e0: f7fe 1a80 3651 3890 a034 116d 305e 5254 ....6Q8..4.m0^RT
000001f0: 6d79 80b9 a50a 9724 0d2d fc36 0d95 55aa my.....$.-.6..U.
boot.bin
is a bootable x86 disk image of exactly 1 sector big.
boot.bin
$ file boot.bin
boot.bin: DOS/MBR boot sector; partition 1 : ID=0x78, active 0xe5, start-CHS (0x76,93,42), end-CHS (0x27c,78,5), startsector 1862423724, 2871009009 sectors; partition 2 : ID=0x2f, active 0x93, start-CHS (0x4a,126,14), end-CHS (0x2a6,185,3), startsector 404829478, 3630435133 sectors
You can run this disk image in qemu using a commandline like this:
qemu-system-x86_64 -hda boot.bin
When you boot the binary in qemu it will tell you: Come back with a modern CPU :P
. This is because the executable uses the cpuid
instructions and checks for a bitmask of 0x19
in the ECX
register. ECX
contains a bitmask with the 'extended features' of the CPU. This means it checks for bit 0, bit 3 and bit 4 being set. If we look up the specifications for the cpuid instruction we learn that:
bit# short description
--------------------------------------------------------
bit0 -- sse3 - Prescott New Instructions-SSE3 (PNI)
bit3 -- monitor - MONITOR and MWAIT instructions (SSE3)
bit4 -- ds-cpl - CPL qualified debug store
So we are probably missing some flag to qemu to enable SSE3
.
qemu-system-x86_64 -cpu Broadwell-noTSX -hda mbr.bin -boot c -nographic
Booting from Hard Disk...
We upgraded from RC4 this year!
Password:
Static approach
Analyzing the binary, we learn that strings are obfuscated. The routine that does so sits at 7CE8h
. The de-obfuscation is simply XOR'ing every byte in a buffer with 0x42
unti la NULL
-byte is encountered, at which point it stops deobfuscating.
If we simply XOR the entire boot.bin with 0x42
, we reveal the following deobfuscated strings:
000000f0: 998f 52a9 b181 0d0a 5765 2075 7067 7261 ..R.....We upgra
00000100: 6465 6420 6672 6f6d 2052 4334 2074 6869 ded from RC4 thi
00000110: 7320 7965 6172 2100 0d0a 5061 7373 776f s year!...Passwo
00000120: 7264 3a20 000d 0a43 6f6d 6520 6261 636b rd: ...Come back
00000130: 2077 6974 6820 6120 6d6f 6465 726e 2043 with a modern C
00000140: 5055 203a 5000 0820 0800 d2d2 d2d2 d2d2 PU :P.. ........
The AES function at 0x7ca3
uses self-modifying code so is a bit unreadable, here is an unrolled standalone implementation:
aes.S
:
.global aeswinkel
.type aeswinkel, %function
.global _start
.intel_syntax noprefix
.macro keyschedule x
aeskeygenassist xmm1, xmm0, \x
pshufd xmm1, xmm1, 0xff
shufps xmm2, xmm0, 0x10
pxor xmm0, xmm2
shufps xmm2, xmm0, 0x8c
pxor xmm0, xmm2
pxor xmm0, xmm1
.endm
aeswinkel:
movaps xmm0, [rsi]
movaps xmm3, [rdi]
pxor xmm2, xmm2
pxor xmm3, xmm0
keyschedule 0x1
aesenc xmm3, xmm0
keyschedule 0x2
aesenc xmm3, xmm0
keyschedule 0x4
aesenc xmm3, xmm0
keyschedule 0x8
aesenc xmm3, xmm0
keyschedule 0x10
aesenc xmm3, xmm0
keyschedule 0x20
aesenc xmm3, xmm0
keyschedule 0x40
aesenc xmm3, xmm0
keyschedule 0x80
aesenc xmm3, xmm0
keyschedule 0x1b
aesenc xmm3, xmm0
keyschedule 0x36
aesenclast xmm3, xmm0
movaps [rdx], xmm3
ret
aes-test.c
:
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <malloc.h>
#include <sys/mman.h>
void aeswinkel(uint8_t *in_a, uint8_t *in_b, uint8_t *out);
int main(int argc, char *argv[]) {
if (argc != 2) {
printf("usage: %s <input>\n", argv[0]);
return -1;
}
if (strlen(argv[1]) != 0x10) {
printf("wrong len\n");
return -2;
}
void* ptr = mmap(
0xA0000000, 0x1000,
PROT_READ | PROT_WRITE | PROT_EXEC,
MAP_FIXED | MAP_PRIVATE | MAP_ANONYMOUS, -1, 0
);
uint8_t *in_a = (uint8_t*)(0xA0000000+0x00);
uint8_t *in_b = (uint8_t*)(0xA0000000+0x10);
uint8_t *out = (uint8_t*)(0xA0000000+0x20);
uint8_t mbr_vector[]={
0x6d, 0x79, 0x80, 0xb9, 0xa5, 0x0a, 0x97, 0x24,
0x0d, 0x2d, 0xfc, 0x36, 0x0d, 0x95, 0x55, 0xaa
};
memcpy(in_a, argv[1], 0x10);
memcpy(in_b, mbr_vector, 0x10);
aeswinkel(in_a, in_b, out);
int i;
printf("OUTPUT: ");
for(i = 0; i < 0x10; i++) {
printf("%02x ", out[i]);
}
printf("\n");
return 0;
}
Compile with gcc -o aes-test -masm=intel aes-test.c.c aes.S
Example invocation:
$ ./aes-test AAAABBBBCCCCDDDD
OUTPUT: 6b e5 00 02 b2 cd 8b 1f 5e 27 75 32 da f7 02 bb
After some more analysis and verification, we concluded it is 10-round AES-128.
We could just decrypt the checked array at 0x7de0
.
000001e0: f7fe 1a80 3651 3890 a034 116d 305e 5254 ....6Q8..4.m0^RT
Python solution:
from Crypto.Cipher import AES
key = [0x6d, 0x79, 0x80, 0xb9, 0xa5, 0x0a, 0x97, 0x24, 0x0d, 0x2d, 0xfc, 0x36, 0x0d, 0x95, 0x55, 0xaa]
v = [0xf7, 0xfe, 0x1a, 0x80, 0x36, 0x51, 0x38, 0x90, 0xa0, 0x34, 0x11, 0x6d, 0x30, 0x5e, 0x52, 0x54]
key = bytes(key)
v = bytes(v)
a = AES.new(key, AES.MODE_ECB)
print(a.decrypt(v).decode('latin-1'))
Password is MiLiT4RyGr4d3MbR
Flag
Flag: AOTW{31oct__1s__25dec}